/*
 * Copyright (c) 2017 - present, CV4J Contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cv4j.core.pixels;

import com.cv4j.core.datamodel.ByteProcessor;
import com.cv4j.core.datamodel.ColorProcessor;
import com.cv4j.core.datamodel.ImageProcessor;
import com.cv4j.exception.CV4JException;
import com.cv4j.image.util.Tools;

/**
 * 调整
 *
 * @author dev
 * @date 2021/07/22
 */
public class Resize {
    /**
     * 最近的intepolate
     */
    public static final int NEAREST_INTEPOLATE = 1;
    /**
     * biline intepolate
     */
    public static final int BILINE_INTEPOLATE = 2;
    /**
     * xrate
     */
    private float xrate;
    /**
     * yrate
     */
    private float yrate;

    /**
     * 调整
     *
     * @param rate 率
     */
    public Resize(float rate) {
        xrate = rate;
        yrate = rate;
    }

    /**
     * 调整
     *
     * @param xrate xrate
     * @param yrate yrate
     */
    public Resize(float xrate, float yrate) {
        this.xrate = xrate;
        this.yrate = yrate;
    }

    /**
     * 调整
     *
     * @param processor 处理器
     * @param type 类型
     * @return {@link ImageProcessor}
     */
    public ImageProcessor resize(ImageProcessor processor, int type) {
        if (type == NEAREST_INTEPOLATE) {
            return nearest(processor);
        } else if (type == BILINE_INTEPOLATE) {
            return biline(processor);
        } else {
            throw new CV4JException("Unsupported resize type...");
        }
    }

    /**
     * 最近的
     *
     * @param processor 处理器
     * @return {@link ImageProcessor}
     */
    private ImageProcessor nearest(ImageProcessor processor) {
        int width = processor.getWidth();
        int height = processor.getHeight();
        int w1 = (int) (width * xrate);
        int h1 = (int) (height * yrate);
        int channels = processor.getChannels();
        ImageProcessor dst = (channels == 3) ? new ColorProcessor(w1, h1) : new ByteProcessor(w1, h1);
        int index = 0;
        for (int row = 0; row < h1; row++) {
            int srcRow = Math.round(((float) row) / yrate);
            if (srcRow >= height) {
                srcRow = height - 1;
            }
            for (int col = 0; col < w1; col++) {
                int srcCol = Math.round(((float) col) / xrate);
                if (srcCol >= width) {
                    srcCol = width - 1;
                }
                int index2 = row * w1 + col;
                index = srcRow * width + srcCol;
                for (int i = 0; i < channels; i++) {
                    dst.toByte(i)[index2] = processor.toByte(i)[index];
                }
            }
        }
        return dst;
    }

    /**
     * biline
     *
     * @param processor 处理器
     * @return {@link ImageProcessor}
     */
    private ImageProcessor biline(ImageProcessor processor) {
        int width = processor.getWidth();
        int height = processor.getHeight();
        int w1 = (int) (width * xrate);
        int h1 = (int) (height * yrate);
        int channels = processor.getChannels();
        ImageProcessor dst = (channels == 3) ? new ColorProcessor(w1, h1) : new ByteProcessor(w1, h1);
        int index = 0;
        for (int row = 0; row < h1; row++) {
            double srcRow = ((float) row) / yrate;
            // 获取整数部分坐标 row Index
            double j1 = Math.floor(srcRow);
            // 获取行的小数部分坐标
            double t1 = srcRow - j1;
            for (int col = 0; col < w1; col++) {
                double srcCol = ((float) col) / xrate;
                // 获取整数部分坐标 column Index
                double k1 = Math.floor(srcCol);
                // 获取列的小数部分坐标
                double u1 = srcCol - k1;
                int[] p1 = getPixel(j1, k1, width, height, processor);
                int[] p2 = getPixel(j1, k1 + 1, width, height, processor);
                int[] p3 = getPixel(j1 + 1, k1, width, height, processor);
                int[] p4 = getPixel(j1 + 1, k1 + 1, width, height, processor);
                double a1 = (1.0d - t1) * (1.0d - u1);
                double b1 = (1.0d - t1) * u1;
                double c1 = (t1) * (1.0d - u1);
                double d1 = t1 * u1;
                index = row * w1 + col;
                for (int i = 0; i < channels; i++) {
                    int pv = (int) (p1[i] * a1 + p2[i] * b1 + p3[i] * c1 + p4[i] * d1);
                    dst.toByte(i)[index] = (byte) Tools.clamp(pv);
                }
            }
        }
        return dst;
    }

    /**
     * 得到像素
     *
     * @param j1 j
     * @param k1 k
     * @param width 宽度
     * @param height 高度
     * @param processor 处理器
     * @return {@link int[]}
     */
    private int[] getPixel(double j1, double k1, int width, int height, ImageProcessor processor) {
        int row = (int) j1;
        int col = (int) k1;

        if (row >= height) {
            row = height - 1;
        }

        if (row < 0) {
            row = 0;
        }

        if (col < 0) {
            col = 0;
        }

        if (col >= width) {
            col = width - 1;
        }

        int index = row * width + col;
        int channels = processor.getChannels();
        int[] rgb = new int[channels];
        for (int i = 0; i < channels; i++) {
            rgb[i] = processor.toByte(i)[index] & 0xff;
        }
        return rgb;
    }
}
